package dst.ass2.di.agent;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.logging.Logger;

import javassist.ByteArrayClassPath;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.NotFoundException;
import dst.ass2.di.annotation.Component;

/**
 * https://schuchert.wikispaces.com/WritingYourOwnJavaAgent
 * http://javapapers.com/core-java/java-instrumentation/ !!!
 *
 */
public class InjectorAgent implements ClassFileTransformer {
	
	private final static Logger LOGGER = Logger.getLogger(InjectorAgent.class.getName());
	
	public static void premain(String args, Instrumentation instrumentation) {
        new InjectorAgent(args, instrumentation);
    }

    private final Instrumentation instrumentation;

    public InjectorAgent(String args, Instrumentation instrumentation) {
        this.instrumentation = instrumentation;
        this.instrumentation.addTransformer(this);
    }
	
	
	@Override
	public byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {

		LOGGER.info("call transform");
		
		String fullClassName = className.replace('/', '.');			//get Classname with . instead of /
		
		LOGGER.info("fullClassName in transform: "+fullClassName);
		
		ClassPool classPool = ClassPool.getDefault();
		classPool.insertClassPath(new ByteArrayClassPath(fullClassName, classfileBuffer));

		//An instance of CtClass represents a class. It is obtained from ClassPool.
		CtClass ctClass = null;
		Component component = null;
		
		try {
			//try to get Class
			ctClass = classPool.get(fullClassName);
			
			//search for Component Annotation
			component = (Component) ctClass.getAnnotation(Component.class); //check if there is an Component
			if (component == null) {		//if not 
				return classfileBuffer;		//we are done
			}
			else{
				//get contructors and insert method call
				CtConstructor[] ctors = ctClass.getConstructors();  //loop over all Constructors
				for (CtConstructor ctor : ctors) {
					try {
						ctor.insertAfter("dst.ass2.di.InjectionControllerFactory.getTransparentInstance().initialize(this);");  //call it to inject
					} catch (CannotCompileException cce) {
						cce.printStackTrace();
						return classfileBuffer;
					}
				}
				
				//after all constructors are modified
				return ctClass.toBytecode();		//return Bytecode of class
			}
		} 
		catch (NotFoundException nfe) {
			System.err.println("Could not open clas: "+nfe.getMessage());
			return classfileBuffer;
		}
		catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
			return classfileBuffer;
		}
		catch (IOException ioe) {
			ioe.printStackTrace();
			return classfileBuffer;
		} 
		catch (CannotCompileException cce) {
			cce.printStackTrace();
			return classfileBuffer;
		}	
		
	}
}
